#include "StartupController.h"

StartupController::StartupController(uint8_t ssPin){
  _ssPin = ssPin;

  if(_ssPin>-1) 
  {
	  digitalWrite(_ssPin, LOW);
	  pinMode(_ssPin, OUTPUT);
  }

  setupSPI();

}

bool StartupController::setupSPI()
{
	SPI.begin ();
	SPI.setClockDivider(SPI_CLOCK_DIV4);

	return(true);
}

bool StartupController::print(char str[])
{
	uint8_t length = strlen(str);

	_chipSelect();
	
	_cTransfer(CMD_PRINT);
	_transfer (length);
	for (uint8_t i = 0 ; i < length; i++) {
		_transfer (str[i]);
	}
	_bufferedCommandEnd();

	_chipDeselect();
	return(true);
}

bool StartupController::print(int number, uint8_t base)
{
	_chipSelect();
	
	_cTransfer(CMD_PRINTNUMBER);
	_transfer (base);
	_transfer (highByte(number));
	_transfer (lowByte(number));
	_bufferedCommandEnd();

	_chipDeselect();
	return(true);
}

bool StartupController::print(int number)
{
	return print(number, DEC);
}

bool StartupController::setCursor(uint8_t column, uint8_t row)
{
	_chipSelect();
	_cTransfer(CMD_CURSOR);
	_transfer(column);
	_transfer(row);
	_bufferedCommandEnd();

	_chipDeselect();
	return(true);
}

bool StartupController::home()
{
	_chipSelect();
	_cTransfer(CMD_HOME);
	_bufferedCommandEnd();

	_chipDeselect();
	return(true);
}

bool StartupController::clear()
{
	_chipSelect();
	_transfer(CMD_CLEAR);
	_bufferedCommandEnd();

	_chipDeselect();
	return(true);
}

bool StartupController::write(char chr){
	_chipSelect();
	_transfer(CMD_WRITE);
	_transfer(chr);
	_bufferedCommandEnd();

	_chipDeselect();
	return(true);
}

bool StartupController::blink(){
	return blinkMode(true);
}

bool StartupController::noBlink(){
	return blinkMode(false);
}

bool StartupController::blinkMode(bool mode){
	_chipSelect();
	_transfer(CMD_BLINKMODE);
	if(mode) {
		_transfer(1);
	}else {
		_transfer(0);
	}
	_bufferedCommandEnd();

	_chipDeselect();
	return(true);
}

bool StartupController::cursor(){
	return cursorMode(true);
}

bool StartupController::noCursor(){
	return cursorMode(false);
}

bool StartupController::cursorMode(bool mode){
	_chipSelect();
	_transfer(CMD_CURSORMODE);
	if(mode) {
		_transfer(1);
	}else {
		_transfer(0);
	}
	_bufferedCommandEnd();

	_chipDeselect();
	return(true);
}

bool StartupController::display(){
	return displayMode(true);
}

bool StartupController::noDisplay(){
	return displayMode(false);
}

bool StartupController::displayMode(bool mode){
	_chipSelect();
	_transfer(CMD_DISPLAYMODE);
	if(mode) {
		_transfer(1);
	}else {
		_transfer(0);
	}
	_bufferedCommandEnd();

	_chipDeselect();
	return(true);
}

bool StartupController::scrollDisplayLeft(){
	return scroll(true);
}

bool StartupController::scrollDisplayRight(){
	return scroll(false);
}

bool StartupController::scroll(bool direction){
	_chipSelect();
	_transfer(CMD_SCROLL);
	if(direction) {
		_transfer(1);
	}else {
		_transfer(0);
	}
	_bufferedCommandEnd();

	_chipDeselect();
	return(true);
}

bool StartupController::autoScroll(){
	return autoScrollMode(true);
}

bool StartupController::noAutoScroll(){
	return autoScrollMode(false);
}

bool StartupController::autoScrollMode(bool mode){
	_chipSelect();
	_transfer(CMD_AUTOSCROLL);
	if(mode) {
		_transfer(1);
	}else {
		_transfer(0);
	}
	_bufferedCommandEnd();

	_chipDeselect();
	return(true);
}

bool StartupController::leftToRight(){
	return textDirection(true);
}

bool StartupController::rightToLeft(){
	return textDirection(false);
}

bool StartupController::textDirection(bool direction){
	_chipSelect();
	_transfer(CMD_TEXTDIRECTION);
	if(direction) {
		_transfer(1);
	}else {
		_transfer(0);
	}
	_bufferedCommandEnd();

	_chipDeselect();
	return(true);
}

bool StartupController::createChar(uint8_t index, uint8_t chr[])
{
	_chipSelect();
	
	_transfer(CMD_CUSTOMCHAR);
	_transfer (index);
	for (uint8_t i = 0 ; i < 8; i++) {
		_transfer (chr[i]);
	}
	_bufferedCommandEnd();

	_chipDeselect();
	return(true);
}

bool StartupController::renderBigNumber(uint8_t number, uint8_t column)
{
	_chipSelect();
	_cTransfer(CMD_RENDERBIGNUMBER);
	_transfer(number);
	_transfer(column);
	_bufferedCommandEnd();

	_chipDeselect();
	return(true);
}

bool StartupController::clearBigNumber(uint8_t column)
{
	_chipSelect();
	_cTransfer(CMD_CLEARBIGNUMBER);
	_transfer(column);
	_bufferedCommandEnd();

	_chipDeselect();
	return(true);
}

bool StartupController::setscreen(uint8_t id){
	_chipSelect();
	_transfer(CMD_SETSCREEN);
		_transfer(id);
	_bufferedCommandEnd();

	_chipDeselect();
	return(true);
}

bool StartupController::begin(uint8_t columns, uint8_t rows)
{
	return setup(columns,rows);
}

bool StartupController::setup(uint8_t columns, uint8_t rows)
{
	_chipSelect();
	_transfer(CMD_SETUP);
	_transfer(columns);
	_transfer(rows);
	_bufferedCommandEnd();

	_chipDeselect();
	return(true);
}

bool StartupController::openLog(char logPrefix[])
{
	uint8_t length = strlen(logPrefix);

	_chipSelect();

	_cTransfer(CMD_LOGOPEN);
	_transfer (length);
	for (uint8_t i = 0 ; i < length; i++) {
		_transfer (logPrefix[i]);
	}
	_bufferedCommandEnd();

	_chipDeselect();
	return(true);
}

bool StartupController::newLog(char logPrefix[])
{
	uint8_t length = strlen(logPrefix);

	_chipSelect();

	_cTransfer(CMD_LOGNEW);
	_transfer (length);
	for (uint8_t i = 0 ; i < length; i++) {
		_transfer (logPrefix[i]);
	}
	_bufferedCommandEnd();

	_chipDeselect();
	return(true);
}

bool StartupController::closeLog()
{
	_chipSelect();

	_cTransfer(CMD_LOGCLOSE);
	_bufferedCommandEnd();

	_chipDeselect();
	return(true);
}

bool StartupController::clearLog()
{
	_chipSelect();

	_cTransfer(CMD_LOGCLEAR);
	_bufferedCommandEnd();

	_chipDeselect();
	return(true);
}

bool StartupController::logWrite(char str[])
{
	uint8_t length = strlen(str);

	_chipSelect();

	_cTransfer(CMD_LOGWRITE);
	_transfer (length);
	for (uint8_t i = 0 ; i < length; i++) {
		_transfer (str[i]);
	}
	_bufferedCommandEnd();

	_chipDeselect();
	return(true);
}


void StartupController::fileOpen(char filename[])
{
	uint8_t length = strlen(filename);
	_chipSelect();
	_cTransfer(CMD_ESC);
	_cTransfer(CMD_FILEOPEN);
	_transfer (length);
	for (uint8_t i = 0 ; i < length; i++) {
		_transfer (filename[i]);
	}
	while(digitalRead(MISO)) {};// hold while controller is busy

	_cTransfer(CMD_ENDINTURRPTCOMMAND);

	_chipDeselect();
}

void StartupController::fileClose(){
	_chipSelect();
	_cTransfer(CMD_ESC);
	_cTransfer(CMD_FILECLOSE);
	while(digitalRead(MISO)) {};// hold while controller is busy

	_cTransfer(CMD_ENDINTURRPTCOMMAND);

	_chipDeselect();
}


bool StartupController::fileReadline(char destination[], uint8_t maxLength, char separator){
	uint8_t readBytes = 0;
	_chipSelect();
	_cTransfer(CMD_ESC);
	_cTransfer(CMD_FILEREADLINE);
	_cTransfer(maxLength);
	_cTransfer(separator);
	while(digitalRead(MISO)) {};// hold while controller is busy
	_cTransfer(CMD_GETDATA); // clear the busy flag

	readBytes = _cTransfer(CMD_GETDATA);
	for (uint8_t i = 0; i < readBytes; ++i) {
		destination[i]=_cTransfer(CMD_GETDATA);
	}

	_cTransfer(CMD_ENDINTURRPTCOMMAND);

	_chipDeselect();

}


void StartupController::fileSeek(uint32_t location){
	_chipSelect();
	_cTransfer(CMD_ESC);
	_cTransfer(CMD_FILESEEK);
	_cTransfer(location>>24);
	_cTransfer(location>>16);
	_cTransfer(location>>8);
	_cTransfer(location);

	while(digitalRead(MISO)) {};// hold while controller is busy

	_cTransfer(CMD_ENDINTURRPTCOMMAND);

	_chipDeselect();
}

bool StartupController::fileExists(char filename[]){
	uint8_t length = strlen(filename);
	bool retVal = false;
	_chipSelect();
	_cTransfer(CMD_ESC);
	_cTransfer(CMD_FILEEXISTS);
	_transfer (length);
	for (uint8_t i = 0 ; i < length; i++) {
		_transfer (filename[i]);
	}
	while(digitalRead(MISO)) {};// hold while controller is busy
	retVal = _cTransfer(CMD_GETDATA);

	_cTransfer(CMD_ENDINTURRPTCOMMAND);

	_chipDeselect();
	return(retVal);
}

uint32_t StartupController::fileSize(char filename[]){
	uint8_t length = strlen(filename);
	uint32_t retVal = 0;
	uint8_t nibble;

	_chipSelect();
	_cTransfer(CMD_ESC);
	_cTransfer(CMD_FILESIZE);
	_transfer (length);
	for (uint8_t i = 0 ; i < length; i++) {
		_transfer (filename[i]);
	}
	while(digitalRead(MISO)) {};// hold while controller is busy
	retVal = _cTransfer(CMD_GETDATA);

	_cTransfer(CMD_GETDATA);//resume

	nibble = _cTransfer(CMD_GETDATA);
	retVal = nibble;

	nibble = _cTransfer(CMD_GETDATA);

	retVal = (retVal<<8) | nibble;

	nibble = _cTransfer(CMD_GETDATA);
	retVal = (retVal<<8) | nibble;

	nibble = _cTransfer(CMD_GETDATA);
	retVal = (retVal<<8) | nibble;


	_cTransfer(CMD_ENDINTURRPTCOMMAND);

	_chipDeselect();
	return(retVal);
}

uint8_t StartupController::getError(){
	uint8_t retVal = 0;
	_chipSelect();
	_cTransfer(CMD_ESC);
	_cTransfer(CMD_GETERROR);
	retVal = _cTransfer(CMD_GETDATA);

	_cTransfer(CMD_ENDINTURRPTCOMMAND);

	_chipDeselect();
	return(retVal);
}

int StartupController::getFreeBuffer()
{
	uint8_t lB,hB = 0;
	int returnVal = 0;
	_chipSelect();
	_cTransfer(CMD_ESC);
	_cTransfer(CMD_GETFREEBUFFER);
	hB = _cTransfer(CMD_GETDATA);
	lB = _cTransfer(CMD_GETDATA);
	_cTransfer(CMD_ENDINTURRPTCOMMAND);

	_chipDeselect();
	returnVal = word(hB,lB);
	return(returnVal);
}

bool StartupController::_transfer(uint8_t byte)
{
    _cTransfer(byte);
	if(byte==CMD_ESC) { // if it's a null byte escape it
		_cTransfer(byte);
	}
	return(true);
}

byte StartupController::_cTransfer(uint8_t byte) // command transfer, without auto null escaping
{
	static uint8_t lastTransmit = 0;
	uint8_t returnValue = 0;
    returnValue = SPI.transfer (byte);
	/* 
	if (returnValue != lastTransmit) {
		Serial.print("mismatch, should be ");
		Serial.print(lastTransmit,HEX);
		Serial.print(" was ");
		Serial.println(returnValue,HEX);
	}*/
	lastTransmit = byte;
	delayMicroseconds (SC_BYTEDELAY); 

	return(returnValue);
}

inline void StartupController::_chipSelect() {
	if(_ssPin > -1 ) {
		digitalWrite(_ssPin, LOW);    // bring chip select low
		delayMicroseconds (SC_SETUPDELAY); // wait for the interrupt to fire
	}
}

inline void StartupController::_chipDeselect() {
	if(_ssPin > -1 ) {
		digitalWrite(_ssPin, HIGH); // disable Slave Select
		delayMicroseconds (SC_POSTDELAY); 
	}
}

inline void StartupController::_bufferedCommandEnd() {
	_cTransfer(CMD_ESC); // tell the controller we're done with the command
	_cTransfer(CMD_ENDBUFFERECOMMAND);
}
